from PySide6.QtWidgets import *
from PySide6.QtCore import *
from PySide6.QtGui import *

class SelectionBox(QGraphicsItem):
    def __init__(
        self,
        id="SELECTION_BOX_1",
        pos=QPointF(0, 0),
        size=QSizeF(100, 50),
        minSize=QSizeF(30, 30),
        limitRect=QRectF(0, 0, 500, 500),
        centerPen=QPen(Qt.gray, 2),
        centerBrush=QBrush(QColor(10, 10, 10, 100)),
        cornerPen=QPen(Qt.black, 2),
        cornerSize=QSizeF(10, 10),
        cornerBrush=QBrush(Qt.white)        
        # 
    ):
        super().__init__()
        
        self.id = id
        self.size = size
        self.centerPen = centerPen
        self.centerBrush = centerBrush
        self.cornerPen = cornerPen
        self.cornerSize = cornerSize
        self.cornerFix = None
        self.refreshCornerFix()
        self.cornerBrush = cornerBrush
        
        self.dragFlag = None
        self.dragDiff = None   
        self.minSize = minSize
        self.limitRect = limitRect
        
        self.setPos(pos)
        self.setAcceptHoverEvents(True)
        
        # four corner rect in item
    def centerRect(self):
        return QRectF(self.itemTopLeft(), self.size)
    
    def topLeftCornerRect(self):
        return QRectF(self.itemTopLeft() - self.cornerFix, self.cornerSize)
    
    def topRightCornerRect(self):
        return QRectF(self.itemTopRight() - self.cornerFix, self.cornerSize)
    
    def bottomLeftCornerRect(self):
        return QRectF(self.itemBottomLeft() - self.cornerFix, self.cornerSize)
    
    def bottomRightCornerRect(self):
        return QRectF(self.itemBottomRight() - self.cornerFix, self.cornerSize)
    
    # four corner point position in item
    def itemTopLeft(self):
        return QPointF(0, 0)
    
    def itemTopRight(self):
        return QPointF(self.size.width(), 0)
    
    def itemBottomLeft(self):
        return QPointF(0, self.size.height())
    
    def itemBottomRight(self):
        return QPointF(self.size.width(), self.size.height())
    
    # four corner point position in scene
    def sceneTopLeft(self):
        return self.mapToScene(self.itemTopLeft())
    
    def sceneTopRight(self):
        return self.mapToScene(self.itemTopRight())
    
    def sceneBottomLeft(self):
        return self.mapToScene(self.itemBottomLeft())
    
    def sceneBottomRight(self):
        return self.mapToScene(self.itemBottomRight())
    
    def refreshCornerFix(self):
        self.cornerFix = QPointF(self.cornerSize.width()/2, self.cornerSize.height()/2)
    
    def boundingRect(self):
        return QRectF(-self.cornerFix, self.size + self.cornerSize)
    
    
    def paint(self, painter, option, widget):
        # draw center rect
        painter.setPen(self.centerPen)
        painter.setBrush(self.centerBrush)
        painter.drawRect(self.centerRect())
        
        # draw all four corner rect
        painter.setPen(self.cornerPen)
        painter.setBrush(self.cornerBrush)
        cornerXRadius = self.cornerFix.x()
        cornerYRadius = self.cornerFix.y()
        
        painter.drawRoundedRect(self.topLeftCornerRect(), cornerXRadius, cornerYRadius)             # top left
        painter.drawRoundedRect(self.topRightCornerRect(), cornerXRadius, cornerYRadius)            # top right
        painter.drawRoundedRect(self.bottomLeftCornerRect(), cornerXRadius, cornerYRadius)          # bottom left
        painter.drawRoundedRect(self.bottomRightCornerRect(), cornerXRadius, cornerYRadius)  
    
        # set mouse style
    def hoverMoveEvent(self, event):
        if self.topLeftCornerRect().contains(event.pos()) or self.bottomRightCornerRect().contains(event.pos()):
            self.setCursor(Qt.SizeFDiagCursor)
            return
        
        if self.topRightCornerRect().contains(event.pos()) or self.bottomLeftCornerRect().contains(event.pos()):
            self.setCursor(Qt.SizeBDiagCursor)
            return
        
        if self.centerRect().contains(event.pos()):
            self.setCursor(Qt.SizeAllCursor)
            return
        
        self.setCursor(Qt.ArrowCursor)
        
    def mousePressEvent(self, event) -> None:
        mousePressPos = event.pos()
        
        if self.topLeftCornerRect().contains(mousePressPos):
            self.dragFlag = "TOP_LEFT"
            self.dragDiff = event.scenePos() - self.sceneTopLeft()
            return 
        
        if self.topRightCornerRect().contains(mousePressPos):
            self.dragFlag = "TOP_RIGHT"
            self.dragDiff = event.scenePos() - self.sceneTopRight()
            return
        
        if self.bottomLeftCornerRect().contains(mousePressPos):
            self.dragFlag = "BOTTOM_LEFT"
            self.dragDiff = event.scenePos() - self.sceneBottomLeft()
            return
        
        if self.bottomRightCornerRect().contains(mousePressPos):
            self.dragFlag = "BOTTOM_RIGHT"
            self.dragDiff = event.scenePos() - self.sceneBottomRight()
            return
        
        if self.centerRect().contains(mousePressPos):
            self.dragFlag = "CENTER"
            self.dragDiff = event.scenePos() - self.sceneTopLeft()
            return
    
    def mouseMoveEvent(self, event):
        mouseMovePos = event.scenePos()
        
        xMinLimit = self.limitRect.x()
        xMaxLimit = xMinLimit + self.limitRect.width()
        yMinLimit = self.limitRect.y()
        yMaxLimit = yMinLimit + self.limitRect.height()
        minWidth = self.minSize.width()
        minHeight = self.minSize.height()
        
        self.prepareGeometryChange()
        
        match self.dragFlag:
            case "TOP_LEFT":
                sceneTopLeft = mouseMovePos - self.dragDiff
                sceneBottomRight = self.sceneBottomRight()
                
                curTLX = sceneTopLeft.x() if sceneTopLeft.x() > xMinLimit else xMinLimit
                curTLX = curTLX if sceneBottomRight.x() - curTLX > minWidth else sceneBottomRight.x() - minWidth
                
                curTLY = sceneTopLeft.y() if sceneTopLeft.y() > yMinLimit else yMinLimit
                curTLY = curTLY if sceneBottomRight.y() - curTLY > minHeight else sceneBottomRight.y() - minHeight
                                
                self.setPos(QPointF(curTLX, curTLY))
                self.size = QSizeF(sceneBottomRight.x() - curTLX, sceneBottomRight.y() - curTLY)
            
            
            case "TOP_RIGHT":
                sceneTopRight = mouseMovePos - self.dragDiff
                sceneBottomLeft = self.sceneBottomLeft()

                curTRX = sceneTopRight.x() if sceneTopRight.x() < xMaxLimit else xMaxLimit
                curTRX = curTRX if curTRX - sceneBottomLeft.x() > minWidth else sceneBottomLeft.x() + minWidth
                
                curTRY = sceneTopRight.y() if sceneTopRight.y() > yMinLimit else yMinLimit
                curTRY = curTRY if sceneBottomLeft.y() - curTRY > minHeight else sceneBottomLeft.y() - minHeight
                
                self.setPos(QPointF(sceneBottomLeft.x(), curTRY))
                self.size = QSizeF(curTRX - sceneBottomLeft.x(), sceneBottomLeft.y() - curTRY)
                
            case "BOTTOM_LEFT":
                sceneBottomLeft = mouseMovePos - self.dragDiff
                sceneTopRight = self.sceneTopRight()
                
                curBLX = sceneBottomLeft.x() if sceneBottomLeft.x() > xMinLimit else xMinLimit
                curBLX = curBLX if sceneTopRight.x() - curBLX > minWidth else sceneTopRight.x() - minWidth 
                
                curBLY = sceneBottomLeft.y() if sceneBottomLeft.y() < yMaxLimit else yMaxLimit
                curBLY = curBLY if curBLY - sceneTopRight.y() > minHeight else sceneTopRight.y() + minHeight
                
                self.setPos(QPointF(curBLX, sceneTopRight.y()))
                self.size = QSizeF(sceneTopRight.x() - curBLX, curBLY - sceneTopRight.y())
                                
            case "BOTTOM_RIGHT":
                sceneBottomRight = mouseMovePos - self.dragDiff
                sceneTopLeft = self.sceneTopLeft()
                
                curBRX = sceneBottomRight.x() if sceneBottomRight.x() < xMaxLimit else xMaxLimit
                curBRX = curBRX if curBRX - sceneTopLeft.x() > minWidth else  sceneTopLeft.x() + minWidth
                
                curBRY = sceneBottomRight.y() if sceneBottomRight.y() < yMaxLimit else yMaxLimit
                curBRY = curBRY if curBRY - sceneTopLeft.y() > minHeight else sceneTopLeft.y() + minHeight
                
                self.setPos(sceneTopLeft)
                self.size = QSizeF(curBRX - sceneTopLeft.x(), curBRY - sceneTopLeft.y())                
                
            case "CENTER":
                sceneTopLeft = mouseMovePos - self.dragDiff
                
                curTLX = sceneTopLeft.x() if sceneTopLeft.x() > xMinLimit else xMinLimit
                curTLX = curTLX if curTLX + self.size.width() < xMaxLimit else xMaxLimit - self.size.width()
                curTLY = sceneTopLeft.y() if sceneTopLeft.y() > yMinLimit else yMinLimit
                curTLY = curTLY if curTLY + self.size.height() < yMaxLimit else yMaxLimit - self.size.height()
                
                self.setPos(QPointF(curTLX, curTLY))
                
        self.update()
        
    def mouseReleaseEvent(self, event):
        logging.info("鼠标在SelectionBox里松开了！")
        self.dragFlag = None
    
    def setState(self, pos, size):
        self.setPos(pos)
        self.size = size
    
    def setSize(self, size):
        self.size = size
    
    def setLimit(self, limit:QRectF):
        self.limitRect = limit

    def getState(self):
        return self.pos(), self.size
    
    def getPos(self):
        return self.pos()
    
    def getX(self):
        return self.pos().x()
    
    def getY(self):
        return self.pos().y()
    
    def getWidth(self):
        return self.size.width()
    
    def getHeight(self):
        return self.size.height()